package com.zhuangjie.mvc.global_lock;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * GlobalLock 处理程序
 *
 * @author zhuangjie
 * @date 2024/04/26
 */
@Component
@Aspect
public class GlobalLockAspect {

    @Autowired
    private Redisson redisson;

    private static final String SPEL_SIGN_HEADER = "#";

    @Pointcut("@annotation(com.zhuangjie.mvc.global_lock.GlobalLock)")
    public void globalLockPointcut() {
    }

    /**
     * 看是否为spEL
     *
     * @param str str
     * @return boolean
     */
    private static boolean isSpELSignHeader(String str) {
        return str != null && str.startsWith(SPEL_SIGN_HEADER);
    }


    @Around("globalLockPointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        GlobalLock globalLock = method.getAnnotation(GlobalLock.class);

        //解析SpEL表达式，在这里args是参数列表，args[0]就指的是第一个参数
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = buildSpELContext(pjp);

        // spEL解析为实值
        String serviceKey = "global-lock::" + Arrays.stream(globalLock.serviceKey()).map(keyItem -> isSpELSignHeader(keyItem) ? parser.parseExpression(keyItem).getValue(context, String.class) : keyItem).collect(Collectors.joining("::"));
        String instanceKey = Arrays.stream(globalLock.instanceKey()).map(keyItem -> isSpELSignHeader(keyItem) ? parser.parseExpression(keyItem).getValue(context, String.class) : keyItem).collect(Collectors.joining(","));

        //利用解析出的key获取锁
        RLock lock = redisson.getLock(serviceKey + "@" + instanceKey);
        lock.lock();
        try {
            return pjp.proceed();
        } finally {
            lock.unlock();
        }
    }

    /**
     * %1/%2生成sp elcontext
     *
     * @param pjp pjp
     * @return {@link EvaluationContext}
     */
    private EvaluationContext buildSpELContext(ProceedingJoinPoint pjp) {
        StandardEvaluationContext context = new StandardEvaluationContext(createGlobalLockSpelContext(pjp));
        //获取方法参数名列表
        String[] paramNames = ((MethodSignature) pjp.getSignature()).getParameterNames();
        //获取方法参数值列表
        Object[] paramValues = pjp.getArgs();
        for (int i = 0; i < paramNames.length; i++) {
            // 设置spEL变量（#XX）
            context.setVariable(paramNames[i], paramValues[i]);
        }
        return context;

    }

    /**
     * 创建上下文#root变量
     *
     * @param pjp pjp
     * @return {@link GlobalLockSpELContext}
     */
    private GlobalLockSpELContext createGlobalLockSpelContext(JoinPoint pjp) {
        GlobalLockSpELContext globalLockSpelContext = new GlobalLockSpELContext();
        Class<?> clazz = pjp.getTarget().getClass();
        Object[] args = pjp.getArgs();
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        globalLockSpelContext.setArgs(args);
        globalLockSpelContext.setClassName(clazz.getSimpleName());
        globalLockSpelContext.setClassFullName(clazz.getName());
        globalLockSpelContext.setMethodName(method.getName());
        return globalLockSpelContext;
    }
}